﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Web;

namespace Mengsk.ProxyDelivery.HTTPHelpers
{
    public class MSHttpRestful
    {
        static System.Net.Http.HttpClient client = new System.Net.Http.HttpClient { Timeout = new TimeSpan(0, 0, 120) };
        public static Regex REG_URL_ENCODING = new Regex(@"%[a-f0-9]{2}");

        public static String PostStringBodyReturnString(string url, string body, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var data = PostStringBodyReturnBytes(url, body, encoding, headers);
            if (encoding != null)
            {
                return encoding.GetString(data);
            }
            return Encoding.UTF8.GetString(data);
        }

        public static byte[] PostStringBodyReturnBytes(string url, string body, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var ret = client.PostAsync(url, new System.Net.Http.StringContent(body, encoding == null ? Encoding.UTF8 : encoding)).Result;
            var data = ret.Content.ReadAsByteArrayAsync().Result;
            if (ret.IsSuccessStatusCode == false)
            {
                throw new Exception("HTTP请求错误:" + ret.StatusCode);
            }
            return data;
        }

        public static String PostBytesBodyReturnString(string url, byte[] body, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var data = PostBytesBodyReturnBytes(url, body, encoding, headers);
            if (encoding != null)
            {
                return encoding.GetString(data);
            }
            return Encoding.UTF8.GetString(data);
        }

        public static byte[] PostBytesBodyReturnBytes(string url, byte[] body, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var ret = client.PostAsync(url, new System.Net.Http.ByteArrayContent(body)).Result;
            if (ret.IsSuccessStatusCode == false)
            {
                throw new Exception("HTTP请求错误:" + ret.StatusCode);
            }
            var data = ret.Content.ReadAsByteArrayAsync().Result;
            return data;
        }

        public static String PostStreamBodyReturnString(string url, Stream s, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var data = PostStreamBodyReturnBytes(url, s, encoding, headers);
            if (encoding != null)
            {
                return encoding.GetString(data);
            }
            return Encoding.UTF8.GetString(data);
        }

        public static byte[] PostStreamBodyReturnBytes(string url, Stream s, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var ret = client.PostAsync(url, new System.Net.Http.StreamContent(s)).Result;
            if (ret.IsSuccessStatusCode == false)
            {
                throw new Exception("HTTP请求错误:" + ret.StatusCode);
            }
            var data = ret.Content.ReadAsByteArrayAsync().Result;
            return data;
        }

        public static string PostUrlEncodeBodyReturnString(string url, IDictionary<string, string> values, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var data = PostUrlEncodeBodyReturnBytes(url, values, encoding, headers);
            if (encoding != null)
            {
                return encoding.GetString(data);
            }
            return Encoding.UTF8.GetString(data);
        }

        public static byte[] PostUrlEncodeBodyReturnBytes(string url, IDictionary<string, string> values, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var content = new System.Net.Http.FormUrlEncodedContent(values);
            content.Headers.ContentType.CharSet = (encoding ?? Encoding.UTF8).BodyName;
            var ret = client.PostAsync(url, content).Result;
            if (ret.IsSuccessStatusCode == false)
            {
                throw new Exception("HTTP请求错误:" + ret.StatusCode);
            }
            var data = ret.Content.ReadAsByteArrayAsync().Result;
            return data;
        }

        public static string PostMultipartFormDataBodyReturnString(string url, IDictionary<string, object> values, IList<string> fileNames, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var data = PostMultipartFormDataBodyReturnBytes(url, values, fileNames, encoding, headers);
            return Encoding.UTF8.GetString(data);
        }

        public static byte[] PostMultipartFormDataBodyReturnBytes(string url, IDictionary<string, object> values, IList<string> fileNames, Encoding encoding = null, IDictionary<string, string> headers = null)
        {
            var content = new System.Net.Http.MultipartFormDataContent();
            int i = 0;
            foreach (var item in values)
            {
                if (item.Value == null)
                {
                    content.Add(new System.Net.Http.StringContent(""), item.Key);
                }
                else if (item.Value.GetType() == typeof(byte[]))
                {
                    var cc = new System.Net.Http.StreamContent(new MemoryStream((byte[])item.Value));
                    cc.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
                    content.Add(cc, item.Key, fileNames[i]);
                    i++;
                }
                else if (item.Value.GetType() == typeof(DateTime))
                {
                    content.Add(new System.Net.Http.StringContent(((DateTime)item.Value).ToString("yyyy-MM-dd HH:mm:ss")), item.Key);
                }
                else if (item.Value is Enum)
                {
                    content.Add(new System.Net.Http.StringContent(((int)item.Value).ToString()), item.Key);
                }
                else if (item.Value is double)
                {
                    content.Add(new System.Net.Http.StringContent(((double)item.Value).ToString("F2")), item.Key);
                }
                else if (item.Value is float)
                {
                    content.Add(new System.Net.Http.StringContent(((float)item.Value).ToString("F2")), item.Key);
                }
                else
                {
                    content.Add(new System.Net.Http.StringContent(item.Value.ToString()), item.Key);
                }
            }

            var ret = client.PostAsync(url, content).Result;
            if (ret.IsSuccessStatusCode == false)
            {
                throw new Exception("HTTP请求错误:" + ret.StatusCode);
            }
            var data = ret.Content.ReadAsByteArrayAsync().Result;
            return data;
        }

        public static byte[] GetUrlEncodeBodyReturnBytes(string url, IDictionary<string, string> values, Encoding encoding = null, IDictionary<string, string> headers = null, string accept = null, string refer = null)
        {
            var ret = client.GetAsync(url).Result;
            if (ret.IsSuccessStatusCode == false)
            {
                throw new Exception("HTTP请求错误:" + ret.StatusCode);
            }
            var data = ret.Content.ReadAsByteArrayAsync().Result;
            return data;
        }

        public static string DoGet(string url, Encoding encoding, string accept = "", string refer = "", Dictionary<string, string> para = null, Dictionary<string, string> headers = null, Dictionary<string, string> cookies = null)
        {
            Uri uri = new Uri(url);
            string requestURL = url;
            if (para != null && para.Count > 0)
            {
                string requestPram = string.Join("&", para.Select(item => item.Key + "=" + UrlEncode(item.Value, Encoding.GetEncoding("utf-8"))));
                requestURL += "?" + requestPram;
            }
            HttpWebRequest httpPostRequest = (HttpWebRequest)(WebRequest.Create(requestURL));
            httpPostRequest.ServicePoint.Expect100Continue = false;
            httpPostRequest.KeepAlive = true;
            httpPostRequest.Method = "GET";
            httpPostRequest.KeepAlive = true;
            httpPostRequest.Timeout = 1000 * 30;
            httpPostRequest.Referer = refer;
            httpPostRequest.Accept = accept;
            httpPostRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0";
            //添加header
            if (headers != null)
            {
                foreach (var item in headers)
                {
                    httpPostRequest.Headers.Add(item.Key, item.Value);
                }
            }

            //添加cookie
            if (cookies != null)
            {
                httpPostRequest.CookieContainer = new CookieContainer();
                foreach (var item in cookies)
                {
                    httpPostRequest.CookieContainer.Add(new Cookie(item.Key, item.Value, "/", uri.Host));
                }
            }

            HttpWebResponse rsp = (HttpWebResponse)httpPostRequest.GetResponse();
            StreamReader reader = null;
            try
            {
                reader = new StreamReader(rsp.GetResponseStream(), encoding);
                string content = reader.ReadToEnd();
                return content;
            }
            finally
            {
                if (reader != null)
                {
                    reader.Close();
                }
                rsp.Close();
            }
        }

        public static string DoGetWithRetry(int retry, string url, Encoding encoding, string accept = "", string refer = "", Dictionary<string, string> para = null, Dictionary<string, string> headers = null, Dictionary<string, string> cookies = null)
        {
            Exception lastException = null;

            //如果是在调试状态则只重试一次
            if (Debugger.IsAttached)
            {
                retry = 1;
            }
            for (int i = 0; i < retry; i++)
            {
                try
                {
                    return DoGet(url, encoding, accept, refer, para, headers, cookies);
                }
                catch (Exception ex)
                {
                    Thread.Sleep((i + 1) * 1500);
                    lastException = ex;
                }
            }
            throw lastException;
        }

        public static string UrlEncode(string str, Encoding e)
        {
            if (str == null)
            {
                return null;
            }

            String stringToEncode = HttpUtility.UrlEncode(str, e).Replace("+", "%20").Replace("*", "%2A").Replace("(", "%28").Replace(")", "%29");
            return REG_URL_ENCODING.Replace(stringToEncode, m => m.Value.ToUpperInvariant());
        }

        public static T DoWithRetry<T>(Func<T> func, int retryCount = 3)
        {
            for (int i = 0; i < retryCount; i++)
            {
                try
                {
                    return func();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message + Environment.NewLine + ex.StackTrace);
                    if (i == retryCount - 1)
                    {
                        throw ex;
                    }
                    Thread.Sleep((i + 1) * 1500);
                }
            }
            throw new Exception("代码应该永远执行不到这里");
        }
    }
}
